/**
 * 
 */
package tp.pr5.cityLoader;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import tp.pr5.City;
import tp.pr5.Direction;
import tp.pr5.Place;
import tp.pr5.Street;
import tp.pr5.cityLoader.cityLoaderExceptions.WrongCityFormatException;
import tp.pr5.items.CodeCard;
import tp.pr5.items.Fuel;
import tp.pr5.items.Garbage;
import tp.pr5.items.Item;


public class CityLoaderFromTxtFile {
	
	private ArrayList<Place> places = null;
	private ArrayList<Street> streets = null;
	
	/**
	 * Creates the different array lists
	 */
	public CityLoaderFromTxtFile()
	{
		this.places = new ArrayList<Place>();
		this.streets = new ArrayList<Street>();
	}
	/**
	 * 
	 * @param file The input stream where the city is stored
	 * @return The city
	 * @throws WrongCityFormatException  When there is some format error in the file (WrongCityFormatException) or some errors in IO operations.
	 * @throws IOException
	 */
	public City loadCity(InputStream file) throws WrongCityFormatException, IOException
	{
	    BufferedReader reader = new BufferedReader(new InputStreamReader(file));
	    
	    String[] parameter = null;
		String line = reader.readLine();
		parameter = line.split(" ");
		boolean placesRead = false;
		boolean streetsRead = false;
		boolean itemsRead = false;
		
		
		if (parameter[0].equalsIgnoreCase("BeginCity"))
		{
			while (!parameter[0].equalsIgnoreCase("EndCity"))
			{
				if (parameter[0].equalsIgnoreCase("BeginPlaces"))
				{
					readPlaces(reader);
					placesRead = true;
				}
				
				if (parameter[0].equalsIgnoreCase("BeginStreets"))
				{
					readStreets(reader);
					streetsRead = true;
				}
				
				if (parameter[0].equalsIgnoreCase("BeginItems"))
				{
					readItems(reader);
					itemsRead = true;
				}
				line = reader.readLine();
				if (line != null)
					parameter = line.split(" ");
				else
					throw new WrongCityFormatException();
			}
		}
		else
			throw new WrongCityFormatException();
		
		if (!placesRead || !streetsRead || !itemsRead)
			throw new WrongCityFormatException();
		
		reader.close();
		
		Street[] aStreets = new Street[streets.size()];
		this.streets.toArray(aStreets);
	
		return new City(aStreets);
	}
	
	private void readPlaces(BufferedReader reader) throws IOException
	{
		try{
			String line = reader.readLine();
			line = prepareLine(line);
			String[] parameter;
			if (line != null)
				parameter = line.split(" ");
			else
				throw new WrongCityFormatException();
			
			int i = 0;
			while (!line.equalsIgnoreCase("EndPlaces"))
			{
				if (parameter.length == 5 && parameter[0].equalsIgnoreCase("Place") && i == Integer.parseInt(parameter[1]))
				{
					boolean isSpaceShip = false;
					if (parameter[4].equalsIgnoreCase("spaceShip"))
					{
						isSpaceShip = true;
					}
					
					places.add(i,new Place(parameter[2], isSpaceShip, parameter[3].replaceAll("_", " ")));
					
					line = reader.readLine();
					line = prepareLine(line);
					if (line != null)
						parameter = line.split(" ");
					else
						throw new WrongCityFormatException();
					i++;
				}
				else
					throw new WrongCityFormatException();
			}
		}
		catch (Exception e)
		{
			throw new WrongCityFormatException();

		}
	}
	
	private void readStreets(BufferedReader reader) throws IOException
	{
		try{
			String line = reader.readLine();
			line = prepareLine(line);
			String[] parameter;
			if (line != null)
				parameter = line.split(" ");
			else
				throw new WrongCityFormatException();
			int i = 0;
			while (!line.equalsIgnoreCase("EndStreets"))
			{
				if ((parameter.length == 8 || parameter.length == 9)  && parameter[0].equalsIgnoreCase("Street") && i == Integer.parseInt(parameter[1]))
				{
					if (this.places.get(Integer.parseInt(parameter[3])) != null && places.get(Integer.parseInt(parameter[6])) != null )
					{
						boolean isOpen = false;
						Direction direction = Direction.valueOf(parameter[4].toUpperCase());
						if (parameter[7].equalsIgnoreCase("open"))
						{
							isOpen = true;
							this.streets.add(Integer.parseInt(parameter[1]), new Street(places.get(Integer.parseInt(parameter[3])), direction, places.get(Integer.parseInt(parameter[6]))));
						}
						else if (parameter[7].equalsIgnoreCase("closed"))
						{
							this.streets.add(Integer.parseInt(parameter[1]), new Street(places.get(Integer.parseInt(parameter[3])), direction, places.get(Integer.parseInt(parameter[6])), isOpen, parameter[8]));
						}
						else
							throw new WrongCityFormatException();
						line = reader.readLine();
						if (line != null)
							parameter = line.split(" ");
						else
							throw new WrongCityFormatException();
						
						i++;
					}
					else
						throw new WrongCityFormatException();
				}
				else
					throw new WrongCityFormatException();
			}
		}
		catch (Exception e)
		{
			throw new WrongCityFormatException();
		}
	}
	
	private void readItems(BufferedReader reader) throws IOException, WrongCityFormatException
	{
		try{
		String line = reader.readLine();
		line = prepareLine(line);
		String[] parameter;
		if (line != null)
			parameter = line.split(" ");
		else
			throw new WrongCityFormatException();
		
		int i = 0;
		while (!line.equalsIgnoreCase("EndItems"))
			{
				if (i != Integer.parseInt(parameter[1]))
					throw new WrongCityFormatException();
	
				if (parameter.length == 8 && parameter[0].equalsIgnoreCase("Fuel"))
				{
					if (places.get(Integer.parseInt(parameter[7])) != null)
					{
						Item item = new Fuel(parameter[2], parameter[3], Integer.parseInt(parameter[4]), Integer.parseInt(parameter[5]));
						places.get(Integer.parseInt(parameter[7])).addItem(item);
					}
					else
						throw new WrongCityFormatException();
				}
				if (parameter.length == 7 && parameter[0].equalsIgnoreCase("Garbage"))
				{
					if (places.get(Integer.parseInt(parameter[6])) != null)
					{
						Item item = new Garbage(parameter[2], parameter[3], Integer.parseInt(parameter[4]));
						places.get(Integer.parseInt(parameter[6])).addItem(item);
					}
					else
						throw new WrongCityFormatException();
				}
				if (parameter.length == 7 && parameter[0].equalsIgnoreCase("CodeCard"))
				{
					if (places.get(Integer.parseInt(parameter[6])) != null)
					{
						Item item = new CodeCard(parameter[2], parameter[3], parameter[4]);
						this.places.get(Integer.parseInt(parameter[6])).addItem(item);
					}
					else
						throw new WrongCityFormatException();
				}
				i++;
				line = reader.readLine();
				if (line != null)
					parameter = line.split(" ");
				else
					throw new WrongCityFormatException();
			}
		}
		catch (Exception e)
		{
			throw new WrongCityFormatException();
		}
	}


	private String prepareLine(String line) {
		if (line != null && line.contains("\""))
		{
			String[] splittedLine = line.split("\"");
			splittedLine[1] = splittedLine[1].replaceAll(" ", "_");
			
			if (splittedLine.length == 3)
				return splittedLine[0]+splittedLine[1]+splittedLine[2];
		}
		return line;
	}

    /**
     * Returns the place where the robot will start the simulation
     * @return The initial place
     */
	public Place getInitialPlace()
	{
		return places.get(0);
	}
}

